// evoperformance.h

#pragma once

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Drawing;
using namespace MentalAlchemy::Atomics;

namespace MentalAlchemy{
namespace Performance {
	#pragma region - IStatsProcessor interface and implementation classes. -
	///
	/// Interface to define operations with stats represented as dicionaries of <value, frequency>.
	//template <typename T>
	public interface class IStatsProcessor
	{
	public:
		///
		/// Performs processing of frequencies table and returns a single value, as result of special characteristic calculation.
		float ProcessSingleValue (Dictionary<System::Byte, int>^ dict);
		IStatsProcessor^ Clone ();
	};

	public ref class MeanCalculator : IStatsProcessor {
	public:
		///
		/// Performs processing of frequencies table and calculates mean value.
		virtual float ProcessSingleValue (Dictionary<System::Byte, int>^ dict);
		virtual IStatsProcessor^ Clone ();
	};

	public ref class VarianceCalculator : IStatsProcessor {
	public:
		///
		/// Performs processing of frequencies table and calculates variance.
		virtual float ProcessSingleValue (Dictionary<System::Byte, int>^ dict);
		virtual IStatsProcessor^ Clone ();
	};

	public ref class EntropyCalculator : IStatsProcessor {
	public:
		///
		/// Performs processing of frequencies table and calculates entropy.
		virtual float ProcessSingleValue (Dictionary<System::Byte, int>^ dict);
		virtual IStatsProcessor^ Clone ();
	};
	#pragma endregion

	namespace Atomics {
		#pragma region - Image related operations. -
		array<float,2>^ Convolve (array<float,2>^ table, array<float,2>^ kernel);
		Array^ CreateBorders(Array^ src, int hBorder, int wBorder);
		unsigned char ValidateIntensityValue(int value);
		#pragma endregion

		#pragma region - Matrix operations. -
		Dictionary<System::Byte, int>^ GetNeighborhoodStats(Array^ src, Point^ coord, const int radius);

		array<float,2>^ CalculateLocalStats(array<System::Byte,2>^ data, const int radius, IStatsProcessor^ procObj);
		array<float,2>^ CalculateLocalStats(array<System::Byte,2>^ data, array<float,2>^ points, const int radius, IStatsProcessor^ procObj);
		float Max(Array^ m);
		float Min(Array^ m);

		void NormalizeSelf (Array^ m);

		Point IndexOfMin (array<float,2>^ data);

		array<float>^ GetRow(array<float, 2>^ m, int idx);
		#pragma endregion

		#pragma region - Vector operations. -
		float Entropy(ICollection<int>^ v);
		int Sum(ICollection<int>^ mas);
		float EuclidianDistance (array<float,1>^ v1, array<float,1>^ v2);
		float EuclidianDistanceSqr (array<float,1>^ v1, array<float,1>^ v2);
		#pragma endregion

		#pragma region - Struct operations. -
		float Mean(Dictionary<System::Byte, int>^ dict);
		float Variance(Dictionary<System::Byte, int>^ dict);
		float Variance(Dictionary<System::Byte, int>^ dict, const float mean);
		int SumCounts (Dictionary<System::Byte, int>^ dict);
		Dictionary<System::Byte, int>^ CreateFullBytesDictionary ();
		array<float, 2>^ CalculateDistances (List<List<Node2D^>^>^ nodes, array<float, 1>^ vector);
		Node2D^ GetClosestNode(List<List<Node2D^>^>^ nodes, array<float, 1>^ v);
		Point GetClosestNodeCoords(List<List<Node2D^>^>^ nodes, array<float, 1>^ v);
		float CalculateError(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
		//List<List<List<int>^>^>^ GetBatches(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
		array<List<int>^, 2>^ GetBatches(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
		#pragma endregion
	}

	namespace MachineLearningElements {
		List<List<TrainingSample^>^>^ ResampleBootstrap(List<TrainingSample^>^ data, int count);
		Dictionary<System::String^, int>^ TestAlgorithm(IClassifier^ alg, IList<TrainingSample^>^ testData);
		array<Fitness^>^ EvaluateNetworks(FitnessFunction^ func, IList<array<float>^>^ weights);
		float TestNetwork(INeuralNetwork^ net, IList<TrainingSample^>^ testData);
		float CalculateMSE (INeuralNetwork^ net, IList<TrainingSample^>^ testData);
		List<array<float,1>^>^ GetOutputs(INeuralNetwork^ net1, List<TrainingSample^>^ data);

		public ref class Bagging {
		public:
			static void Train (List<TrainingSample^>^ data, List<IClassifier^>^ clrs);
		};
	}

	public ref class ImageMaths
	{
	public:
		static array<float,2>^ Convolve (array<float,2>^ table, array<float,2>^ kernel);
	};

	public ref class VectorMath
	{
	public:
		static int IndexOfMax(array<float, 1>^ v);
	};

	public ref class MatrixMath
	{
	public:
		static Dictionary<System::Byte, int>^ GetNeighborhoodStats(Array^ src, Point^ coord, int radius);

		static array<float, 2>^ CalculateLocalStats(array<System::Byte, 2>^ data, const int radius, IStatsProcessor^ procObj);

		static array<float, 2>^ CalculateLocalStats(array<System::Byte, 2>^ data, array<float,2>^ points, const int radius, IStatsProcessor^ procObj);

		static void NormalizeSelf (Array^ m);

		static Point IndexOfMin (array<float,2>^ data);

		static array<float>^ GetRow(array<float, 2>^ m, int idx);
	};

	public ref class StructMath
	{
	public:
		static Array^ CalculateDistances (List<List<Node2D^>^>^ nodes, array<float, 1>^ vector);

		static float CalculateError(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
		static array<List<int>^, 2>^ GetBatches(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
		//static List<List<List<int>^>^>^ GetBatches(List<List<Node2D^>^>^ nodes, List<array<float, 1>^>^ trainingData);
	};

	public ref class MachineLearning 
	{
	public:
		static void TrainBagging (List<TrainingSample^>^ data, List<IClassifier^>^ clrs);
		static Dictionary<System::String^, int>^ TestAlgorithm(IClassifier^ alg, IList<TrainingSample^>^ testData);
		static array<Fitness^>^ EvaluateNetworks(FitnessFunction^ func, IList<array<float>^>^ weights);
		static float TestNetwork(INeuralNetwork^ net, IList<TrainingSample^>^ testData);
		static float CalculateMSE(INeuralNetwork^ net, IList<TrainingSample^>^ testData);
		static List<array<float,1>^>^ GetOutputs(INeuralNetwork^ net1, List<TrainingSample^>^ data);
	};
}
}
